package tools

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"os/exec"
)
import "golang.org/x/text/encoding/simplifiedchinese"

type ShellHelper struct {
}

//阻塞式的执行外部shell命令的函数,等待执行完毕并返回标准输出
func (this *ShellHelper) ExecCommandWithOutput(s string) (string, error) {
	//函数返回一个*Cmd，用于使用给出的参数执行name指定的程序
	cmd := exec.Command("/bin/bash", "-c", s)

	//读取io.Writer类型的cmd.Stdout，再通过bytes.Buffer(缓冲byte类型的缓冲器)将byte类型转化为string类型(out.String():这是bytes类型提供的接口)
	var out bytes.Buffer
	cmd.Stdout = &out

	//Run执行c包含的命令，并阻塞直到完成。  这里stdout被取出，cmd.Wait()无法正确获取stdin,stdout,stderr，则阻塞在那了
	err := cmd.Run()
	checkErr(err)
	return out.String(), err
}

func (this *ShellHelper) ExecCommandWithLineOutput(commandName string, params []string) (arrResult []string, err error) {
	arrResult = nil
	//函数返回一个*Cmd，用于使用给出的参数执行name指定的程序
	cmd := exec.Command(commandName, params...)
	//显示运行的命令
	//fmt.Println("show command arg:" + strings.Join(cmd.Args, " "))
	//StdoutPipe方法返回一个在命令Start后与命令标准输出关联的管道。Wait方法获知命令结束后会关闭这个管道，一般不需要显式的关闭该管道。
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		//fmt.Println("cmd.StdoutPipe error：" + err.Error())
		return nil, err
	}
	err = cmd.Start()
	if err != nil {
		//fmt.Println("cmd.Start error：" + err.Error())
		return nil, err
	}
	//创建一个流来读取管道内内容，这里逻辑是通过一行一行的读取的
	reader := bufio.NewReader(stdout)
	tmpByte := []byte{13, 10}

	//实时循环读取输出流中的一行内容
	for {
		line, err2 := reader.ReadBytes('\n')

		if err2 != nil || io.EOF == err2 {
			//tmpstr := this.ConvertByte2String(line, "GB18030")
			//fmt.Println("执行命令进度：6" + tmpstr)
			//fmt.Println(err2)
			return nil, err2
		}
		//fmt.Println("执行命令进度：7")
		//fmt.Println(string(line))
		str := this.ConvertByte2String(line, "GB18030")
		//fmt.Println("输出行：" + str)
		if len(str) > 0 && line[0] != tmpByte[0] {
			arrResult = append(arrResult, str)
		}
	}
	//fmt.Println("执行命令进度：8")
	//阻塞直到该命令执行完成，该命令必须是被Start方法开始执行的
	cmd.Wait()
	//fmt.Println("执行命令进度：9")
	return arrResult, nil
}

//不需要执行命令的结果与成功与否，执行命令马上就返回
//func ExecCommandWithoutOutput(command string) {
//	//处理启动参数，通过空格分离 如：setsid /home/luojing/gotest/src/test_main/iwatch/test/while_little &
//	splite_command:=true
//	command_name_and_args := strings.FieldsFunc(command, splite_command)
//	//开始执行c包含的命令，但并不会等待该命令完成即返回
//	cmd.Start()
//	if err != nil {
//		fmt.Printf("%v: exec command:%v error:%v\n", get_time(), command, err)
//	}
//	fmt.Printf("Waiting for command:%v to finish...\n", command)
//	//阻塞等待fork出的子进程执行的结果，和cmd.Start()配合使用[不等待回收资源，会导致fork出执行shell命令的子进程变为僵尸进程]
//	err = cmd.Wait()
//	if err != nil {
//		fmt.Printf("%v: Command finished with error: %v\n", get_time(), err)
//	}
//	return
//}

func checkErr(err error) {
	if err != nil {
		fmt.Println(err)
		panic(err)
	}
}

type Charset string

const (
	UTF8    = Charset("UTF-8")
	GB18030 = Charset("GB18030")
)

func (this *ShellHelper) ConvertByte2String(byte []byte, charset Charset) string {

	var str string
	switch charset {
	case GB18030:
		var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
		str = string(decodeBytes)
	case UTF8:
		fallthrough
	default:
		str = string(byte)
	}

	return str
}
